#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QTableWidget>
#include <QLineEdit>
#include <QLabel>
#include <QTreeWidget>
#include <QProgressDialog>
#include <QThread>
#include <QEventLoop>

#include <sstream>
#include <map>
#include <set>
#include <functional>
using namespace std;

#include "global.h"

#include "lexical/ReManager.h"
#include "lexical/FABuilder.h"
#include "lexical/CodeAnalyzer.h"

#include "syntax/LRMap.h"
#include "syntax/Parser.h"
#include "syntax/ProductionManager.h"
#include "syntax/TokenManager.h"

QT_BEGIN_NAMESPACE
namespace Ui
{
    class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);

    ~Widget();

signals:
    void finished();

private slots:

    void setBtnDisabled();

    void on_openReBtn_clicked();

    void on_runGenFABtn_clicked();

    void on_reTextEdit_textChanged();

    void on_runGenCodeBtn_clicked();

    void on_saveCodeBtn_clicked();

    void on_openCodeBtn_clicked();

    void on_codeTextEdit_textChanged();

    void on_runGenExeBtn_clicked();

    void on_sourceTextEdit_textChanged();

    void on_openSourceBtn_clicked();

    void on_runExeBtn_clicked();

    void on_saveResultBtn_clicked();

    void on_openProdBtn_clicked();

    void on_prodTextEdit_textChanged();

    void on_runProdBtn_clicked();

    void on_openTokenBtn_clicked();

    void on_tokenTextEdit_textChanged();

    void on_runAnalyzeBtn_clicked();

    void on_openBuildTypeBtn_clicked();

    void on_prodBuildTypeTextEdit_textChanged();

private:
    // 自动机相关
    void clearTab();
    void initTab(const string &reName);
    void addNFATab(const string &reName);
    void addDFATab(const string &reName);
    void addMinDFATab(const string &reName);
    void addTabWithTableAndLineEdits(QTabWidget *tabWidget, const QString &tabName);
    void fillStartAndEndPoints(QWidget *tabPage, const QString &tabName, size_t start, set<size_t> ends);
    // 因为需要传引用到FABuilder中，才能获取到每个自动机的数据（原来的代码只是打印了中间过程，没有保存中间过程），所以定义如下很多变量来存中间结果
    set<string> symbols; // 所有符号 // 理论上symbols在三种自动机中也不一样，但是由于DFA和MinDFA都是用map对应的，没有影响，所以就不每个都单独一个变量了
    // ends和start在NFA、DFA、MinDFA中不一样，需要分开存储
    set<size_t> NFAEnds;
    size_t NFAStart = 0;
    set<size_t> DFAEnds;
    size_t DFAStart = 0;
    set<size_t> MinDFAEnds;
    size_t MinDFAStart = 0;
    // NFA
    vector<NFANode> NFANodes; // 所有NFA节点
    // DFA
    map<size_t, map<string, size_t>> DFANodes; // 将DFANodesTemp重新索引后的结果
    // MinDFA
    map<size_t, map<string, size_t>> MinDFANodes; // 最小化DFA点集
    // 保存FABuilder生成的结果，待后续生成cpp文件内容
    map<string, pair<map<size_t, map<string, size_t>>, set<size_t>>> DFAsAndEndsOfTokenType; // 所有正则名带_前缀的DFA图和对应的终点集
    map<string, pair<map<size_t, map<string, size_t>>, set<size_t>>> DFAsAndEndsOfAuxiliary;
    // ^^^所有会出现在reOfTokenType的正则值中的正则名的DFA图和对应的终点集

    // 词法分析程序相关
    QString codeFilePath;  // 用户选择的分词程序代码路径，因为选择文件按钮和生成exe文件按钮是分开的，所以需要保存前者选择的路径让后者可以使用命令行生成exe程序
    bool isExeGen = false; // 标记是否生成了exe程序，为true并且待分析代码内容不为空时启用执行分词程序按钮

    // 文法相关
    void initAnalyzeTableAndNodes(ProductionManager productionManager, bool isLA);
    bool isProdGen = false; // 标记是否解析文法语句并生成了分析表，为true并且分词结果内容不为空时启用执行语法分析按钮
    LRMap lrMap;            // 由于执行逻辑分散，需要保存中间结果
    void buildGTree(GNode node, QTreeWidgetItem *father);

    // 通用
    QString openFile(QString title, QString types);
    QString readFile(QString filePath);
    void saveFile(const QString &content, const QString &fileSuffix);
    void showLoading(QString title, QString info);
    void closeLoading();
    vector<string> toBuffs(string content);
    void fillTableWithHeadersAndData(QTableWidget *tableWidget,
                                     const std::vector<std::string> &headers,
                                     const std::vector<std::vector<std::string>> &data);
    void doInThread(function<void()> func);
    QProgressDialog *loadingDialog; // 加载中进度条弹框

    Ui::Widget *ui;
};
#endif // WIDGET_H
